package cn.duyo.spring.context.support;

import cn.duyo.spring.beans.BeansException;
import cn.duyo.spring.beans.factory.ConfigurableListableBeanFactory;
import cn.duyo.spring.beans.factory.config.BeanFactoryPostProcessor;
import cn.duyo.spring.beans.factory.config.BeanPostProcessor;
import cn.duyo.spring.context.ApplicationContext;
import cn.duyo.spring.context.ApplicationEvent;
import cn.duyo.spring.context.ApplicationListener;
import cn.duyo.spring.context.ConfigurableApplicationContext;
import cn.duyo.spring.context.event.ApplicationEventMulticaster;
import cn.duyo.spring.context.event.ContextClosedEvent;
import cn.duyo.spring.context.event.ContextRefreshedEvent;
import cn.duyo.spring.context.event.SimpleApplicationEventMulticaster;
import cn.duyo.spring.core.convert.ConversionService;
import cn.duyo.spring.core.io.DefaultResourceLoader;

import java.util.Collection;
import java.util.Map;

/**
 * 应用上下文抽象类实现
 * tips: 继承 DefaultResourceLoader 是为了处理 spring.xml 配置资源的加载
 * AbstractApplicationContext ——> ConfigurableApplicationContext ——> ApplicationContext -> ListableBeanFactory -> BeanFactory
 *
 * @author du
 */
public abstract class AbstractApplicationContext extends DefaultResourceLoader implements ConfigurableApplicationContext {

    public static final String APPLICATION_EVENT_MULTICASTER_BEAN_NAME = "applicationEventMulticaster";

    private ApplicationEventMulticaster applicationEventMulticaster;

    @Override
    public void refresh() throws BeansException {
        // 1. 创建 BeanFactory，并加载 BeanDefinition (内部会加载资源，解析为 BeanDefinition 并注册)
        refreshBeanFactory();

        // 2. 获取 BeanFactory
        ConfigurableListableBeanFactory beanFactory = getBeanFactory();

        // 3. 添加 ApplicationContextAwareProcessor，让继承自 ApplicationContextAware 的 Bean 对象都能感知所属的 ApplicationContext
        // TODO !!! 上下文在bean创建时并无法直接拿到 (接口设计并无继承或组合等关联性, 包也不同)，所以通过包装成 BeanPostProcessor 来传递上下文
        beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));

        // 注册可解析依赖
        // TODO ApplicationContext 并不是一个常规bean，并不能直接在外部进行注入，但注册可解析依赖后则无需去bean容器寻找并直接返回，避免了从bean容器中找。
        //  ApplicationContext 本身不是通过 Bean 定义管理的对象，但通过此机制可将其作为依赖注入到其他 Bean 中，避免循环依赖问题‌。@Autowire 等就是这么做的
        //  其他框架组件（如 BeanFactory、ResourceLoader）也通过此方式注册
        beanFactory.registerResolvableDependency(ApplicationContext.class, this);

        // 4. 在 Bean 实例化之前，执行 BeanFactoryPostProcessor (Invoke factory processors registered as beans in the context.)，主要是可以修改 BeanDefinition
        invokeBeanFactoryPostProcessors(beanFactory);

        // 5. BeanPostProcessor 需要提前于其他 Bean 对象实例化之前执行注册操作(放到一个集合，后续触发执行)
        registerBeanPostProcessors(beanFactory);


        /**
         * (涉及6/7/9)
         * spring观察者模式采用事件驱动 (订阅-发布)，核心三个:
         * 1. 事件：顶级事件为 ApplicationEvent，继承自java EventObject
         * 2. 订阅者(监听器)：ApplicationListener，继承自java标记接口 EventListener
         * 3. 发布者：ApplicationEventPublisher，顶级上下文 ApplicationContext 接口本身实现了这个接口，所以 AbstractApplicationContext 拥有 publishEvent 方法
         * 4. 事件广播器：ApplicationEventMulticaster ——> AbstractApplicationEventMulticaster  ——> SimpleApplicationEventMulticaster
         *    类似观察者模式中的Subject，维护一组订阅者，并触发事件通知
         */
        // 6. 初始化事件发布者 (用于实例化一个 SimpleApplicationEventMulticaster，这是一个事件广播器)
        initApplicationEventMulticaster();

        // 7. 注册事件监听器 (通过 getBeansOfType 方法获取到所有从 spring.xml 中加载到的事件配置 Bean 对象)
        registerListeners();

        // 8. 设置类型转换器、提前实例化单例Bean对象
        finishBeanFactoryInitialization(beanFactory);

        // 8. 提前实例化单例Bean对象 (TODO 移动到类型转换器 )
        //beanFactory.preInstantiateSingletons();

        // 9. 发布容器刷新完成事件 (发布了第一个服务器启动完成后的事件，这个事件通过 publishEvent 发布出去，其实内部是调用了 applicationEventMulticaster.multicastEvent(event);)
        //    publishEvent 是给给外部程序使用的，multicastEvent是内部使用，其本质也是在循环调用所有订阅者(监听器)的 onApplicationEvent 方法
        //    最后close中，增加了发布一个容器关闭事件。publishEvent(new ContextClosedEvent(this));
        finishRefresh();
    }


    // 设置类型转换器、提前实例化单例Bean对象
    protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
        // TODO 设置类型转换器（要在实例化单例bean之前，否则拿不到类型转换服务），如此在初始化属性赋值时、以及@Value替换Bean时
        if (beanFactory.containsBean("conversionService")) {
            Object conversionService = beanFactory.getBean("conversionService");
            if (conversionService instanceof ConversionService) {
                beanFactory.setConversionService((ConversionService) conversionService);
            }
        }

        // 提前实例化单例Bean对象
        beanFactory.preInstantiateSingletons();
    }

    @Override
    public void registerShutdownHook() {
        // 注册JVM虚拟机优雅关机钩子
        Runtime.getRuntime().addShutdownHook(new Thread(this::close));
    }

    @Override
    public void close() {
        // 发布容器关闭事件
        publishEvent(new ContextClosedEvent(this));

        // 执行销毁单例bean的销毁方法
        getBeanFactory().destroySingletons();
    }

    protected abstract void refreshBeanFactory() throws BeansException;

    protected abstract ConfigurableListableBeanFactory getBeanFactory();

    private void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
        Map<String, BeanFactoryPostProcessor> beanFactoryPostProcessorMap = beanFactory.getBeansOfType(BeanFactoryPostProcessor.class);
        for (BeanFactoryPostProcessor beanFactoryPostProcessor : beanFactoryPostProcessorMap.values()) {
            beanFactoryPostProcessor.postProcessBeanFactory(beanFactory);
        }
    }

    private void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFactory) {
        Map<String, BeanPostProcessor> beanPostProcessorMap = beanFactory.getBeansOfType(BeanPostProcessor.class);
        for (BeanPostProcessor beanPostProcessor : beanPostProcessorMap.values()) {
            beanFactory.addBeanPostProcessor(beanPostProcessor);
        }
    }

    private void initApplicationEventMulticaster() {
        ConfigurableListableBeanFactory beanFactory = getBeanFactory();
        applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
        beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, applicationEventMulticaster);
    }

    @SuppressWarnings({"rawtypes"})
    private void registerListeners() {
        Collection<ApplicationListener> applicationListeners = getBeansOfType(ApplicationListener.class).values();
        for (ApplicationListener listener : applicationListeners) {
            applicationEventMulticaster.addApplicationListener(listener);
        }
    }

    private void finishRefresh() {
        publishEvent(new ContextRefreshedEvent(this));
    }

    /**
     * 这是 spring 所有事件的触发入口
     */
    @Override
    public void publishEvent(ApplicationEvent event) {
        applicationEventMulticaster.multicastEvent(event);
    }

    @Override
    public <T> Map<String, T> getBeansOfType(Class<T> type) throws BeansException {
        return getBeanFactory().getBeansOfType(type);
    }

    @Override
    public String[] getBeanDefinitionNames() {
        return getBeanFactory().getBeanDefinitionNames();
    }

    @Override
    public Object getBean(String name) throws BeansException {
        return getBeanFactory().getBean(name);
    }

    @Override
    public Object getBean(String name, Object... args) throws BeansException {
        return getBeanFactory().getBean(name, args);
    }

    @Override
    public <T> T getBean(String name, Class<T> requiredType) throws BeansException {
        return getBeanFactory().getBean(name, requiredType);
    }

    @Override
    public <T> T getBean(Class<T> requiredType) throws BeansException {
        return getBeanFactory().getBean(requiredType);
    }

    @Override
    public boolean containsBean(String name) {
        return getBeanFactory().containsBean(name);
    }
}
